package cn._2dland.websocket;

import java.io.IOException;
import java.net.Socket;
import java.util.Hashtable;
import java.util.Map;

import cn._2dland.websocket.handler.IConnectionHandler;
import cn._2dland.websocket.io.MessageInputStream;
import cn._2dland.websocket.io.MessageInputStreamV00;
import cn._2dland.websocket.io.MessageInputStreamV08;
import cn._2dland.websocket.io.MessageOutputStream;
import cn._2dland.websocket.io.MessageOutputStreamV00;
import cn._2dland.websocket.io.MessageOutputStreamV08;
import cn._2dland.websocket.model.handshake.HandShakeRequest;
import cn._2dland.websocket.model.handshake.HandShakeResponse;
import cn._2dland.websocket.model.message.BinaryMessage;
import cn._2dland.websocket.model.message.ControlMessage;
import cn._2dland.websocket.model.message.Message;
import cn._2dland.websocket.model.message.TextMessage;

/**
 * WebSocket连接
 * 
 * @author deadblue
 */
public class WebSocketConnection {

	/** 连接状态：初始状态，已创建TCP连接，尚未握手 */
	public static final int STATE_INITIAL = 0;
	/** 连接状态：开始握手 */
	public static final int STATE_HANDSHAKING = 1;
	/** 连接状态：握手成功，正式开始会话 */
	public static final int STATE_CONNECTED = 2;
	/** 连接状态：准备关闭连接 */
	public static final int STATE_CLOSING = 3;
	/** 连接状态：连接已关闭，收到此事件时，连接已不可用 */
	public static final int STATE_CLOSED = 4;

	/** 生命周期线程类 */
	private static class Lifecycle extends Thread {

		/** 连接对象*/
		private WebSocketConnection conn = null;

		public Lifecycle(WebSocketConnection conn) {
			this.conn = conn;
		}

		@Override
		public void run() {
			// 设置状态为开始握手，并尝试握手
			conn.state = STATE_HANDSHAKING;

			// 若握手成功，则进入开始进入生命周期
			if(conn.handshake()) {
				// 更新状态
				conn.state = STATE_CONNECTED;
				// 事件通知
				conn.handleOpen();
				
				// 当连接状态变为准备关闭时，跳出循环
				while(conn.state != STATE_CLOSING) {
					// 读取一个报文，发生异常时则视为关闭报文
					Message msg = null;
					try {
						msg = conn.input.readMessage();
						if(msg == null)
							msg = new ControlMessage(ControlMessage.CLOSE);
					} catch (Exception e) {
						msg = new ControlMessage(ControlMessage.CLOSE);
					}
					
					// 处理报文
					if(msg instanceof ControlMessage) {
						// 控制类消息的处理
						ControlMessage cm = (ControlMessage) msg;

						switch(cm.getCommand()) {
						case ControlMessage.PING:
							// 心跳检查
							conn.send(new ControlMessage(ControlMessage.PONG));
							break;
						case ControlMessage.CLOSE:
							// 客户端请求关闭连接
							conn.state = STATE_CLOSING;
							break;
						}
					} else if (msg instanceof TextMessage) {
						// 事件通知
						TextMessage tm = (TextMessage) msg;
						conn.handleTextMessage(tm);
					} else if (msg instanceof BinaryMessage) {
						// 事件通知
						BinaryMessage bm = (BinaryMessage) msg;
						conn.handleBinaryMessage(bm);
					}
				}
			}
			

			try {
				// 释放资源
				conn.input.close();
				conn.output.close();
				conn.socket.close();
			} catch (Exception e) {}

			// 将状态修改为已关闭
			conn.state = STATE_CLOSED;
			// 事件通知
			conn.handleClose();
		}

	};

	/** 客户端状态 */
	private int state = STATE_INITIAL;
	/** 客户端套接字 */
	private Socket socket = null;
	/** 事件监听器列表 */
	private Map<Integer, IConnectionHandler> handlers = null;

	/** 报文输入流，在握手后创建 */
	private MessageInputStream input = null;
	/** 报文输出流，在握手后创建 */
	private MessageOutputStream output = null;

	/**
	 * 构造器
	 * @param server 服务器对象
	 * @param socket 套接字
	 */
	public WebSocketConnection(WebSocketServer server, Socket socket) {
		this.socket = socket;

		// 初始化状态
		state = STATE_INITIAL;
		handlers = new Hashtable<Integer, IConnectionHandler>();
	}

	/**
	 * 添加事件处理器
	 * @param handler
	 */
	public void addHandler(IConnectionHandler handler) {
		handlers.put(handler.hashCode(), handler);
	}

	/**
	 * 移除事件监听器
	 * @param handler
	 */
	public void removeHandler(IConnectionHandler handler) {
		handlers.remove(handler.hashCode());
	}

	/**
	 * 启动连接，开始生命周期
	 */
	public void start() {
		// 开始生命周期，将在单独的线程中执行
		Lifecycle life = new Lifecycle(this);
		life.start();
	}

	/**
	 * 获取连接状态
	 * @return
	 */
	public int getState() {
		return state;
	}

	/**
	 * 连接是否可用
	 * @return
	 */
	public boolean isActive() {
		return state == STATE_CONNECTED;
	}
	
	/**
	 * 连接是否关闭
	 * 
	 * @return
	 */
	public boolean isClose() {
		return (state == STATE_CLOSED || state == STATE_CLOSING);
	}
	
	/**
	 * 发送文本报文
	 * @param message
	 */
	public void sendText(String text) {
		if(isActive())
			send(new TextMessage(text));
	}

	/**
	 * 发送二进制报文
	 * @param binary
	 */
	public void sendBinary(byte[] binary) {
		if(isActive())
			send(new BinaryMessage(binary));
	}

	/**
	 * 断开连接
	 */
	public void close() {
		if(!isClose()) {
			state = STATE_CLOSING;
			send(new ControlMessage(ControlMessage.CLOSE));
		}
	}

	/**
	 * 发送报文
	 * @param msg
	 */
	private void send(Message msg) {
		try {
			output.writeMessage(msg);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 握手成功通知
	 */
	private void handleOpen() {
		final WebSocketConnection conn = this;

		// 在单独的线程中进行通知
		Thread th = new Thread() {
			@Override
			public void run() {
				for(Map.Entry<Integer, IConnectionHandler> entry : handlers.entrySet()) {
					IConnectionHandler handler = entry.getValue();
					try {
						handler.onOpen(conn);
					} catch (Exception e) {}
				}
			}
		};
		th.start();
	}
	
	/**
	 * 文本报文事件通知
	 * @param msg
	 */
	private void handleTextMessage(TextMessage msg) {
		final WebSocketConnection conn = this;
		final String text = msg.getText();

		Thread th = new Thread() {
			@Override
			public void run() {
				for(Map.Entry<Integer, IConnectionHandler> entry : handlers.entrySet()) {
					IConnectionHandler handler = entry.getValue();
					try {
						handler.onTextMessage(conn, text);
					} catch (Exception e) {}
				}
			}
		};
		th.start();
	}

	/**
	 * 二进制报文事件通知
	 * @param msg
	 */
	private void handleBinaryMessage(BinaryMessage msg) {
		final WebSocketConnection conn = this;
		final byte[] binary = msg.getBinary();

		Thread th = new Thread() {
			@Override
			public void run() {
				for(Map.Entry<Integer, IConnectionHandler> entry : handlers.entrySet()) {
					IConnectionHandler handler = entry.getValue();
					try {
						handler.onBinaryMessage(conn, binary);
					} catch (Exception e) {}
				}
			}
		};
		th.start();
	}

	/**
	 * 关闭事件通知
	 */
	private void handleClose() {
		// 调用监听器的关闭事件
		for(Map.Entry<Integer, IConnectionHandler> entry : handlers.entrySet()) {
			IConnectionHandler handler = entry.getValue();
			try {
				handler.onClose(this);
			} catch (Exception e) {}
		}
	}
	
	/**
	 * 执行握手
	 */
	private boolean handshake() {
		boolean bOK = false;

		try {
			// 读取数据，构造请求头
			HandShakeRequest request = HandShakeRequest.read(socket.getInputStream());
			// 生成响应头
			HandShakeResponse response = new HandShakeResponse(request);
			response.write(socket.getOutputStream());

			// 根据请求版本，封装对应的输入流/输出流
			if(request.isDraft()) {
				input = new MessageInputStreamV00(socket.getInputStream());
				output = new MessageOutputStreamV00(socket.getOutputStream());
			} else {
				input = new MessageInputStreamV08(socket.getInputStream());
				output = new MessageOutputStreamV08(socket.getOutputStream());
			}
			bOK = true;
		} catch (Exception e) {
			bOK = false;
		}
		
		return bOK;
	}

}
